/**
 *
 * @author Juan Pablo
 *
 */
public class ConjuntoCache {
	// atributos de clase
	private LineaCache[] conjunto;
	private int indice;
	private static RAM memoria;
	private static LineaCache reemplazada;

	/**
	 * inicializa el objeto ConjuntoCache
	 */
	public ConjuntoCache() {
		conjunto = new LineaCache[Utilidades.LINEASPORCONJUNTO];
		indice = 0;
	}

	/**
	 * metodo para referenciar a la memoria
	 *
	 * @param ram,
	 *            memoria tipo RAM
	 */
	static void setMemoria(RAM ram) {
		memoria = ram;
	}

	/**
	 * inserta en este conjunto la linea deseada
	 *
	 * @param linea,
	 *            objeto tipo LineaCache
	 */
	public void insertar(LineaCache linea) {
		if (conjunto[indice] == null) {
			conjunto[indice] = linea;
		} else {
			reemplazada = conjunto[indice];
			actualizar(conjunto[indice]);
			conjunto[indice] = linea;
		}
		indice++;
		if (indice == 4) {
			indice = 0;
		}
	}

	/**
	 * metodo para actualizar una linea de cache en la ram
	 *
	 * @param lineaCache,
	 *            linea tipo LineaCache que se va a actualizar
	 */
	private void actualizar(LineaCache lineaCache) {
		lineaCache.setActualizado(true);
		int[] bloque = lineaCache.getBloque();
		int linea = Integer.parseInt("" + lineaCache.getGrupo()
				+ lineaCache.getTag())
				* Utilidades.NUMEROCONJUNTOS * Utilidades.PALABRASPORLINEA;
		memoria.escribirBloque(linea, bloque);
	}

	/**
	 * metood para obtener el conjunto
	 *
	 * @return, un arreglo de tipo LineaCache
	 */
	public LineaCache[] getConjunto() {
		return conjunto;
	}

	/**
	 * metodo para actualizar las lineas desactualizadas de este conjunto
	 */
	public void actualizarConjunto() {
		for (int i = 0; i < Utilidades.LINEASPORCONJUNTO; i++) {
			if (conjunto[i] != null) {
				if (conjunto[i].getActualizado() == false) {
					actualizar(conjunto[i]);
				}
			}
		}
	}

	/**
	 * metodo para buscar tag, para saber si hay acierto de la ram
	 *
	 * @param tag,
	 *            entero con el valor del tag a buscar
	 * @return, boolean que indica si se encontra o no
	 */

	public boolean buscarTag(int tag) {
		for (int i = 0; i < Utilidades.LINEASPORCONJUNTO; i++) {
			if (conjunto[i] != null) {
				if (conjunto[i].getTag() == tag) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * reemplazar una linea en la cache, por una que viene de la ram
	 *
	 * @param nuevaLinea,
	 *            linea que viene de la ram en caso de ser modificada
	 */
	public void reemplazar(LineaCache nuevaLinea) {
		int tag = nuevaLinea.getTag();
		for (int i = 0; i < conjunto.length; i++) {
			if (conjunto[i] != null) {
				if (conjunto[i].getTag() == tag) {
					conjunto[i] = nuevaLinea;
					nuevaLinea.setActualizado(true);
				}
			} else {

			}
		}
	}

	/**
	 * metodo para construir la matriz de este conjunto
	 *
	 * @return, matriz de objetos que corresponde a este conjunto
	 */
	public Object[][] construirMatriz() {
		Object[][] matriz = new Object[4][5];
		int[] aux;
		for (int j = 0; j < conjunto.length; j++) {
			if (conjunto[j] != null) {
				aux = conjunto[j].getLinea();
				for (int i = 0; i < Utilidades.PALABRASPORLINEA + 1; i++) {
					matriz[j][i] = aux[i];
				}
			} else {
				matriz[j][0] = -1;
				matriz[j][1] = 0;
				matriz[j][2] = 0;
				matriz[j][3] = 0;
				matriz[j][4] = 0;
			}
		}
		return matriz;
	}

	/**
	 * obtener las lineas actualizadas
	 *
	 * @return, arreglo de booleanos con la informacion de cada posicion
	 */
	public boolean[] getActualizados() {
		boolean[] actualizados = new boolean[4];
		for (int i = 0; i < conjunto.length; i++) {
			if (conjunto[i] != null) {
				actualizados[i] = conjunto[i].getActualizado();
			} else {
				actualizados[i] = true;
			}
		}
		return actualizados;
	}

	/**
	 * obtener el indice
	 *
	 * @return, retorna el valor del indice para saber en que linea va este
	 *          conjunto
	 */
	public int getIndice() {
		return indice;
	}

	/**
	 * obtener la ultima linea que fue reemplazada
	 *
	 * @return, LineaCache que fue reemplazada
	 */
	static LineaCache reemplazada() {
		return reemplazada;
	}
	/**
	 * Obtener una linea de este conjunto de acuerdo a un tag
	 * @param tag, valor del tag de la linea a obtener
	 * @return, objeto tipo LineaCache
	 */
	public LineaCache getLinea(int tag) {
		for (int i = 0; i < conjunto.length; i++) {
			if (conjunto[i] != null) {
				if (conjunto[i].getTag() == tag) {
					return conjunto[i];
				}
			}
		}
		return null;
	}
}
